#include <riscv/regs.h>

#define ENUM_REG(i) Reg::i
#define ENUM_FREG(i) FReg::i
#define CASE(i) case i:

#define CASE_REGS_2(case1, case2) \
  CASE(case1) CASE(case2)

#define CASE_REGS_3(case1, case2, case3) \
  CASE(case1) CASE(case2) CASE(case3)

#define CASE_REGS_4(case1, case2, case3, case4) \
  CASE(case1) CASE(case2) CASE(case3) CASE(case4)

#define CASE_FREG_RET_LAST(case1, case_last) \
  CASE_REGS_2(ENUM_FREG(case1), ENUM_FREG(case_last)) return #case_last

namespace riscv {
  Reg Regs::get_arg_reg(int index) {
    Assert(index >= 0 && index <= 7, "arg reg out range:%d", index);
    Reg arg_regs[8] = {
      Reg::a0, Reg::a1, Reg::a2, Reg::a3, Reg::a4, Reg::a5, Reg::a6, Reg::a7
    };
    return arg_regs[index];
  }

  FReg Regs::get_arg_freg(int index) {
    Assert(index >= 0 && index <= 7, "arg freg out range:%d", index);
    FReg arg_fregs[8] = {
      FReg::fa0, FReg::fa1, FReg::fa2, FReg::fa3, FReg::fa4, FReg::fa5, FReg::fa6, FReg::fa7
    };
    return arg_fregs[index];
  }

  std::string Regs::get_reg_name(Reg reg) {
    switch(reg) {
    CASE_REGS_2(Reg::x0, Reg::zero) return "zero";
    CASE_REGS_2(Reg::x1, Reg::ra) return "ra";
    CASE_REGS_2(Reg::x2, Reg::sp) return "sp";
    CASE_REGS_2(Reg::x3, Reg::gp) return "gp";
    CASE_REGS_2(Reg::x4, Reg::tp) return "tp";
    CASE_REGS_2(Reg::x5, Reg::t0) return "t0";
    CASE_REGS_2(Reg::x6, Reg::t1) return "t1";
    CASE_REGS_2(Reg::x7, Reg::t2) return "t2";
    CASE_REGS_3(Reg::x8, Reg::s0, Reg::fp) return "s0";
    CASE_REGS_2(Reg::x9, Reg::s1) return "s1";
    CASE_REGS_2(Reg::x10, Reg::a0) return "a0";
    CASE_REGS_2(Reg::x11, Reg::a1) return "a1";
    CASE_REGS_2(Reg::x12, Reg::a2) return "a2";
    CASE_REGS_2(Reg::x13, Reg::a3) return "a3";
    CASE_REGS_2(Reg::x14, Reg::a4) return "a4";
    CASE_REGS_2(Reg::x15, Reg::a5) return "a5";
    CASE_REGS_2(Reg::x16, Reg::a6) return "a6";
    CASE_REGS_2(Reg::x17, Reg::a7) return "a7";
    CASE_REGS_2(Reg::x18, Reg::s2) return "s2";
    CASE_REGS_2(Reg::x19, Reg::s3) return "s3";
    CASE_REGS_2(Reg::x20, Reg::s4) return "s4";
    CASE_REGS_2(Reg::x21, Reg::s5) return "s5";
    CASE_REGS_2(Reg::x22, Reg::s6) return "s6";
    CASE_REGS_2(Reg::x23, Reg::s7) return "s7";
    CASE_REGS_2(Reg::x24, Reg::s8) return "s8";
    CASE_REGS_2(Reg::x25, Reg::s9) return "s9";
    CASE_REGS_2(Reg::x26, Reg::s10) return "s10";
    CASE_REGS_2(Reg::x27, Reg::s11) return "s11";
    CASE_REGS_2(Reg::x28, Reg::t3) return "t3";
    CASE_REGS_2(Reg::x29, Reg::t4) return "t4";
    CASE_REGS_2(Reg::x30, Reg::t5) return "t5";
    CASE_REGS_2(Reg::x31, Reg::t6) return "t6";
    default: Panic("undefined reg: %d", static_cast<int>(reg)); return "";
    }
  }

  std::string Regs::get_reg_name(FReg reg) {
    switch(reg) {
      CASE_FREG_RET_LAST(f0, ft0);
      CASE_FREG_RET_LAST(f1, ft1);
      CASE_FREG_RET_LAST(f2, ft2);
      CASE_FREG_RET_LAST(f3, ft3);
      CASE_FREG_RET_LAST(f4, ft4);
      CASE_FREG_RET_LAST(f5, ft5);
      CASE_FREG_RET_LAST(f6, ft6);
      CASE_FREG_RET_LAST(f7, ft7);
      CASE_FREG_RET_LAST(f8, fs0);
      CASE_FREG_RET_LAST(f9, fs1);
      CASE_FREG_RET_LAST(f10, fa0);
      CASE_FREG_RET_LAST(f11, fa1);
      CASE_FREG_RET_LAST(f12, fa2);
      CASE_FREG_RET_LAST(f13, fa3);
      CASE_FREG_RET_LAST(f14, fa4);
      CASE_FREG_RET_LAST(f15, fa5);
      CASE_FREG_RET_LAST(f16, fa6);
      CASE_FREG_RET_LAST(f17, fa7);
      CASE_FREG_RET_LAST(f18, fs2);
      CASE_FREG_RET_LAST(f19, fs3);
      CASE_FREG_RET_LAST(f20, fs4);
      CASE_FREG_RET_LAST(f21, fs5);
      CASE_FREG_RET_LAST(f22, fs6);
      CASE_FREG_RET_LAST(f23, fs7);
      CASE_FREG_RET_LAST(f24, fs8);
      CASE_FREG_RET_LAST(f25, fs9);
      CASE_FREG_RET_LAST(f26, fs10);
      CASE_FREG_RET_LAST(f27, fs11);
      CASE_FREG_RET_LAST(f28, ft8);
      CASE_FREG_RET_LAST(f29, ft9);
      CASE_FREG_RET_LAST(f30, ft10);
      CASE_FREG_RET_LAST(f31, ft11);
    default: Panic("undefined freg: %d", static_cast<int>(reg)); return "";
    }
  }
}
